/******************************************************************************************
文件名：tcp_comm.c
作者：狼狼    版本：V2.0    创建日期：2021.8.3
文件描述：
    TCP通信会话。
历史修改记录：
1. 2021-8-3:V2.0 狼狼
创建。
2. 2021-9-1:V2.0 狼狼
调整tcp_communicate_process函数中的select超时时间（2s -> 3s）。
3. 2022-5-11:V1.0 狼狼
代码公开。
******************************************************************************************/
//+------------------------------------------------------------------------------------------+
//|                                            前言
//+------------------------------------------------------------------------------------------+
//|      每个TCP通信都有唯一的描述符，因此也就可以基于此描述符建立其唯一的运行实例，围绕该描述
//|  符支持的操作，将相关的方法函数和参数存储到某结构中，就可以使用相同的方式对多个TCP会话进行
//|  管理。
//+------------------------------------------------------------------------------------------+
//|                                          文件说明
//+------------------------------------------------------------------------------------------+
//|      本程序实现了基于一个既有TCP通信描述符创建基本会话实例的功能主体，每个TCP会话实例包含
//|  应用层接收和发送两个缓冲区、相关的参数以及该会话的状态信息，可供上层应用执行监控操作。
//|      另外，考虑到上层应用的监控可能并不存在，因此TCP会话实例本身需要具备基本的异常处理与容
//|  错机制，这些可能的异常来自套接字、缓冲区或其它系统调用的错误状态或返回值。
//|      [程序员说]当前程序暂未添加收发缓冲区使能/禁用接口和缓冲区容量设置/调整接口。
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/#include <stdio.h>
/*|*/#include <string.h>
/*|*/#include <stdlib.h>
/*|*/#include <stdint.h>
/*|*/#include <unistd.h>
/*|*/#include <signal.h>
/*|*/#include <errno.h>
/*|*/#include <sys/socket.h>
/*|*/#include <netinet/in.h>
/*|*/#include <arpa/inet.h>
//|
/*|*/#include "tcp_comm.h"
/*|*/#include "project_debug.h"
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
/*|*/static uint32_t session_base_id = 1000;
//+------------------------------------------------------------------------------------------+
//|  函数名称：stop
//|  功能描述：关闭当前套接字会话处理（子）线程运行许可
//|  参数说明：指向TCP套接字会话结构的指针
//|  返回值说明：成功返回TCPCOMM_SUCCESS，失败返回错误码（负值）
//|  备注：
//+------------------------------------------------------------------------------------------+
static int stop(struct tcp_comm * const ptcpcomm_obj)
{
    /* 参数检查 */
    if(ptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK && ptcpcomm_obj->state != TCPCOMM_STAT_EXIT)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[stop]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    ptcpcomm_obj->flag_run = false;
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：send_data
//|  功能描述：将数据添加到TCP通信会话的发送缓冲区中
//|  参数说明：指向TCP套接字会话结构的指针，指向源数据的指针，数据长度
//|  返回值说明：成功返回实际拷贝的数据长度，失败返回错误码（负值）
//|  备注：注意：若返回0表示数据拷贝长度为0。
//+------------------------------------------------------------------------------------------+
static int send_data(struct tcp_comm * const ptcpcomm_obj, const uint8_t *pdata, uint32_t len)
{
    int ret = 0;
    
    /* 参数检查 */
    if(ptcpcomm_obj == NULL || pdata == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    if(len == 0)
    {
        OUTPUT("[TCPCOMM-ERROR]Data length is 0!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[send_data]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 函数调用 */
    ret = ptcpcomm_obj->tx_buf.add_data(&ptcpcomm_obj->tx_buf, pdata, len);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM:       return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:      return TCPCOMM_ERROR_UNEXPECT_STATE;
        case RINGBUF_ERROR_UNEXPECT_DATA_LENGTH:return TCPCOMM_ERROR_UNEXPECT_DATA_LENGTH;
        default:break;
    }
    
    return ret;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：get_data_len
//|  功能描述：配合数据指针获取函数，获取当前数据指针指向的有效数据长度
//|  参数说明：指向TCP套接字会话结构的指针，数据长度输出指针
//|  返回值说明：成功返回TCPCOMM_SUCCESS，失败返回错误码（负值）
//|  备注：
//+------------------------------------------------------------------------------------------+
static int get_data_len(struct tcp_comm * const ptcpcomm_obj, uint32_t *plen)
{
    int ret = 0;
    
    /* 参数检查 */
    if(ptcpcomm_obj == NULL || plen == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[get_data_len]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 函数调用 */
    ret = ptcpcomm_obj->rx_buf.get_data_len(&ptcpcomm_obj->rx_buf, plen);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：get_data_point
//|  功能描述：配合有效数据长度获取函数，获取当前数据首地址指针
//|  参数说明：指向TCP套接字会话结构的指针，指向数据首地址指针的指针
//|  返回值说明：成功返回TCPCOMM_SUCCESS，失败返回错误码（负值）
//|  备注：
//+------------------------------------------------------------------------------------------+
static int get_data_point(struct tcp_comm * const ptcpcomm_obj, uint8_t **ppdata)
{
    int ret = 0;
    
    /* 参数检查 */
    if(ptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    if(ppdata == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Level 2 pointer can't be NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    if(*ppdata != NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Level 1 pointer must be NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[get_data_point]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 函数调用 */
    ret = ptcpcomm_obj->rx_buf.get_data_point(&ptcpcomm_obj->rx_buf, ppdata);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：update_data_point
//|  功能描述：配合数据长度获取函数，更新读数据指针位置
//|  参数说明：指向TCP套接字会话结构的指针，更新长度
//|  返回值说明：成功返回TCPCOMM_SUCCESS，失败返回错误码（负值）
//|  备注：
//+------------------------------------------------------------------------------------------+
static int update_data_point(struct tcp_comm * const ptcpcomm_obj, uint32_t len)
{
    int ret = 0;
    
    /* 参数检查 */
    if(ptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[update_data_point]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 函数调用 */
    ret = ptcpcomm_obj->rx_buf.update_data_point(&ptcpcomm_obj->rx_buf, len);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM:         return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:        return TCPCOMM_ERROR_UNEXPECT_STATE;
        case RINGBUF_ERROR_UNEXPECT_UPDATE_LENGTH:return TCPCOMM_ERROR_UNEXPECT_UPDATE_LENGTH;
        default:break;
    }
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：wait_for_data
//|  功能描述：等待缓冲区非空
//|  参数说明：指向TCP套接字会话结构的指针
//|  返回值说明：若缓冲区中有数据则返回TCPCOMM_SUCCESS，无数据则等待，失败返回错误码（负值）
//|  备注：
//+------------------------------------------------------------------------------------------+
static int wait_for_data(struct tcp_comm * const ptcpcomm_obj)
{
    int ret = 0;
    
    /* 参数检查 */
    if(ptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[wait_for_data]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 函数调用 */
    ret = ptcpcomm_obj->rx_buf.wait_for_data(&ptcpcomm_obj->rx_buf);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：clear_tx_buf
//|  功能描述：清空发送缓冲区
//|  参数说明：指向TCP套接字会话结构的指针
//|  返回值说明：成功返回TCPCOMM_SUCCESS，失败返回错误码（负值）
//|  备注：清空期间会导致对缓冲区的访问方法返回失败。
//+------------------------------------------------------------------------------------------+
static int clear_tx_buf(struct tcp_comm * const ptcpcomm_obj)
{
    int ret = 0;
    
    /* 参数检查 */
    if(ptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[clear_tx_buf]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 首先禁用缓冲区 */
    ret = ptcpcomm_obj->tx_buf.switch_to_disable(&ptcpcomm_obj->tx_buf);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    /* 清空缓冲区 */
    ret = ptcpcomm_obj->tx_buf.clear(&ptcpcomm_obj->tx_buf);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    /* 最后重新启用缓冲区 */
    ret = ptcpcomm_obj->tx_buf.switch_to_enable(&ptcpcomm_obj->tx_buf);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：clear_rx_buf
//|  功能描述：清空接收缓冲区
//|  参数说明：指向TCP套接字会话结构的指针
//|  返回值说明：成功返回TCPCOMM_SUCCESS，失败返回错误码（负值）
//|  备注：清空期间会导致对缓冲区的访问方法返回失败。
//+------------------------------------------------------------------------------------------+
static int clear_rx_buf(struct tcp_comm * const ptcpcomm_obj)
{
    int ret = 0;
    
    /* 参数检查 */
    if(ptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Pointer is NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 套接字状态检查 */
    if(ptcpcomm_obj->state != TCPCOMM_STAT_OK)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support operation[clear_rx_buf]!\n", ptcpcomm_obj->session_id, ptcpcomm_obj->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 首先禁用缓冲区 */
    ret = ptcpcomm_obj->rx_buf.switch_to_disable(&ptcpcomm_obj->rx_buf);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    /* 清空缓冲区 */
    ret = ptcpcomm_obj->rx_buf.clear(&ptcpcomm_obj->rx_buf);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    /* 最后重新启用缓冲区 */
    ret = ptcpcomm_obj->rx_buf.switch_to_enable(&ptcpcomm_obj->rx_buf);
    switch(ret)
    {
        case RINGBUF_ERROR_ILLEGAL_PARAM: return TCPCOMM_ERROR_ILLEGAL_PARAM;
        case RINGBUF_ERROR_UNEXPECT_STATE:return TCPCOMM_ERROR_UNEXPECT_STATE;
        default:break;
    }
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：tcp_communicate_sub_process
//|  功能描述：TCP通信处理子线程
//|  参数说明：指向套接字对象结构的指针
//|  返回值说明：无
//|  备注：对于每一通信处理子线程实例，本线程需完成以下内容：
//|        1.若应用层发送缓冲区非空，则将缓冲区中的数据发送到连接对端。
//+------------------------------------------------------------------------------------------+
static void *tcp_communicate_sub_process(void *p)
{
    int ret = 0;
    uint8_t *pdata = NULL;
    uint32_t data_len = 0;
    
    /* 获取传参 */
    ST_TCP_COMM *ptcpcomm_obj = (ST_TCP_COMM *)p;
    
    /* 线程处理循环 */
    while(ptcpcomm_obj->flag_run)
    {
        /* 阻塞等待直到缓冲区有数据 */
        ret = ptcpcomm_obj->tx_buf.wait_for_data(&ptcpcomm_obj->tx_buf);
        if(ret != RINGBUF_SUCCESS)
        {
            // ----------------------------------------
            // [说明]若运行标志被关闭，则认为外界触发了
            //       关闭操作；否则为缓冲区内部错误。
            // ----------------------------------------
            if(ptcpcomm_obj->flag_run)
            {
                OUTPUT("[TCPCOMM-ERROR][ID:%d]Wait for TX_BUF data failed with %#x.\n", ptcpcomm_obj->session_id, ret);
                ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_TX_BUF_ERROR;
            }
            else
            {
                ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_DISCONNECT_OR_CLOSE;
            }
            break;
        }
        
        /* 获取缓冲区中的数据长度及读指针 */
        ret = ptcpcomm_obj->tx_buf.get_data_len(&ptcpcomm_obj->tx_buf, &data_len);
        if(ret < 0)
        {
            OUTPUT("[TCPCOMM-ERROR][ID:%d]Get TX_BUF data length failed with %#x.\n", ptcpcomm_obj->session_id, ret);
            ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_TX_BUF_ERROR;
            break;
        }
        
        pdata = NULL;
        ret = ptcpcomm_obj->tx_buf.get_data_point(&ptcpcomm_obj->tx_buf, &pdata);
        if(ret < 0)
        {
            OUTPUT("[TCPCOMM-ERROR][ID:%d]Get TX_BUF data point failed with %#x.\n", ptcpcomm_obj->session_id, ret);
            ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_TX_BUF_ERROR;
            break;
        }
        
        /* 执行send系统调用（使用非阻塞方式） */
SEND:   ret = send(ptcpcomm_obj->comm_sock, pdata, data_len, MSG_NOSIGNAL | MSG_DONTWAIT);
        if(ret < 0)
        {
            if((errno==EAGAIN) || (errno==EWOULDBLOCK) || (errno==EINTR))
            {
                goto SEND;
            }
            OUTPUT("[TCPCOMM-ERROR][ID:%d]Send failed with %d.\n", ptcpcomm_obj->session_id, ret);
            perror("[Error Details]");
            ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_SEND_ERROR;
            break;
        }
        
        /* 更新缓冲区读指针位置 */
        // ----------------------------------------
        // [说明]send非阻塞调用如果成功，将返回实际
        //       拷贝到协议栈发送缓冲区的字节数，本
        //       程序中禁用了通信套接字发送缓冲区，
        //       因此返回值为实际发送的字节数。
        // [考虑]协议栈缓冲区的存在为“少量多次数据
        //       发送时，TCP将其合并为一包数据”提供
        //       了支持，不过应用层缓冲区也可以做到
        //       类似的事情，因此问题不大。
        // ----------------------------------------
        ret = ptcpcomm_obj->tx_buf.update_data_point(&ptcpcomm_obj->tx_buf, ret);
        if(ret < 0)
        {
            OUTPUT("[TCPCOMM-ERROR][ID:%d]Update TX_BUF data point failed with %#x.\n", ptcpcomm_obj->session_id, ret);
            ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_TX_BUF_ERROR;
            break;
        }
    }
    
    /* TCP通信子线程结束动作（无） */
    // ----------------------------------------
    // [说明]通信子线程不需要主动通知通信线程自
    //       身退出的消息，因为通信线程会在每个
    //       循环周期内轮询通信子线程的存在性。
    // ----------------------------------------
    
    pthread_exit(NULL);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：tcp_communicate_process
//|  功能描述：TCP通信处理线程
//|  参数说明：指向套接字对象结构的指针
//|  返回值说明：无
//|  备注：对于每一通信处理线程实例，本线程需完成以下内容：
//|        1.若对端连接有数据传来，则将接收到的数据存入应用层接收缓冲区；
//|          若接收缓冲区已满，则暂停对套接字数据的接收，延时等待一段时间后重试。
//+------------------------------------------------------------------------------------------+
static void *tcp_communicate_process(void *p)
{
    int ret = 0;
    uint32_t unused_space_len = 0;
    
    fd_set comm_sock_readfds;
    struct timeval timeoutval;
    
    uint8_t *prx_cache = NULL;
    // ----------------------------------------
    // [说明]rx_cache_len变量存在复用：
    //       线程初始化时为缓冲区缓存申请长度，
    //       用于接收时为当前数据接收长度。
    // ----------------------------------------
    uint32_t rx_cache_len = 0;
    
    /* 获取传参 */
    ST_TCP_COMM *ptcpcomm_obj = (ST_TCP_COMM *)p;
    
    /* 设置线程分离 */
    pthread_detach(pthread_self());
    
    /* 创建接收Cache区域 */
    // ----------------------------------------
    // [说明]为适配recv系统调用和缓冲区数据存储
    //       接口特性，申请一个小区域做数据转存。
    // [注意]缓存区域必须与缓冲区容量相比较取两
    //       者较低值为其大小。
    // ----------------------------------------
    ret = ptcpcomm_obj->rx_buf.get_unused_space(&ptcpcomm_obj->rx_buf, &unused_space_len);
    if(ret < 0)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]Get RX_BUF unused space failed with %#x.\n", ptcpcomm_obj->session_id, ret);
        ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_RX_BUF_ERROR;
        goto TCPCOMM_EXIT_0;
    }
    (unused_space_len >= RECV_CACHE_LEN) ? (rx_cache_len = RECV_CACHE_LEN) : (rx_cache_len = unused_space_len);
    prx_cache = (uint8_t *)malloc(rx_cache_len);
    if(prx_cache == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]Malloc[%d] RX_CACHE failed!\n", ptcpcomm_obj->session_id, rx_cache_len);
        ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_CACHE_FAIL;
        goto TCPCOMM_EXIT_0;
    }
    
    /* 创建处理子线程 */
    if(pthread_create(&ptcpcomm_obj->tid2, NULL, tcp_communicate_sub_process, (void *)ptcpcomm_obj) != 0)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]Create TCP sub communicate thread failed!\n", ptcpcomm_obj->session_id);
        ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_CACHE_FAIL;
        goto TCPCOMM_EXIT_1;
    }
    
    /* 线程处理循环 */
    while(ptcpcomm_obj->flag_run)
    {
        /* 获取接收缓冲区剩余空间大小 */
        ret = ptcpcomm_obj->rx_buf.get_unused_space(&ptcpcomm_obj->rx_buf, &unused_space_len);
        if(ret < 0)
        {
            OUTPUT("[TCPCOMM-ERROR][ID:%d]Get RX_BUF unused space failed with %#x.\n", ptcpcomm_obj->session_id, ret);
            ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_RX_BUF_ERROR;
            break;
        }
        
        if(unused_space_len == 0)
        {
            usleep(500000);
            continue;
        }
        else
        {
            // ----------------------------------------
            // [注意]设计时需保证任意时刻缓缓冲区剩余空
            //       间必须大于等于实际转存长度。
            // ----------------------------------------
            (unused_space_len >= RECV_CACHE_LEN) ? (rx_cache_len = RECV_CACHE_LEN) : (rx_cache_len = unused_space_len);
        }
        
        /* 执行select等待数据到来 */
        FD_ZERO(&comm_sock_readfds);
        FD_SET(ptcpcomm_obj->comm_sock, &comm_sock_readfds);
        timeoutval.tv_sec  = 3;
        timeoutval.tv_usec = 0;
        ret = select(ptcpcomm_obj->comm_sock + 1, &comm_sock_readfds, NULL, NULL, &timeoutval);
        if(ret < 0)
        {
            OUTPUT("[TCPCOMM-ERROR][ID:%d]Select error!\n", ptcpcomm_obj->session_id);
            ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_SELECT_ERROR;
            break;
        }
        else if(ret==0)
        {
            continue;
        }
        else
        {
            int recv_ret = 0;
            
            /* 执行recv系统调用（使用非阻塞方式） */
RECV:       recv_ret = recv(ptcpcomm_obj->comm_sock, prx_cache, rx_cache_len, MSG_NOSIGNAL | MSG_DONTWAIT);
            if(recv_ret < 0)
            {
                if((errno==EAGAIN) || (errno==EWOULDBLOCK) || (errno==EINTR))
                {
                    goto RECV;
                }
                OUTPUT("[TCPCOMM-ERROR][ID:%d]recv failed with %d.\n", ptcpcomm_obj->session_id, recv_ret);
                perror("[Error Details]");
                ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_RECV_ERROR;
                break;
            }
            else if(recv_ret == 0)
            {
                OUTPUT("[TCPCOMM-INFO][ID:%d]TCP disconnected!\n", ptcpcomm_obj->session_id);
                ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_DISCONNECT_OR_CLOSE;
                break;
            }
            else
            {
                /* 将数据转存至应用层接收缓冲区 */
                ret = ptcpcomm_obj->rx_buf.add_data(&ptcpcomm_obj->rx_buf, prx_cache, recv_ret);
                if(ret < 0)
                {
                    OUTPUT("[TCPCOMM-ERROR][ID:%d]Add data to RX_BUF failed with %#x.\n", ptcpcomm_obj->session_id, ret);
                    ptcpcomm_obj->exit_state = TCPCOMM_EXIT_STAT_RECV_ERROR;
                    break;
                }
            }
        }
        
        /* 判断TCP通信子线程是否存在 */
        if(pthread_kill(ptcpcomm_obj->tid2, 0) != 0)
        {
            OUTPUT("[TCPCOMM-ERROR][ID:%d]TCP communicate sub process thread does not exist!\n", ptcpcomm_obj->session_id);
            break;
        }
    }
    
    /* TCP通信线程结束动作 */
    ptcpcomm_obj->flag_run = false;
    // ----------------------------------------
    // [说明]禁用缓冲区将解除对内部条件变量的阻
    //       塞等待。
    // ----------------------------------------
    ptcpcomm_obj->tx_buf.switch_to_disable(&ptcpcomm_obj->tx_buf);
    ptcpcomm_obj->rx_buf.switch_to_disable(&ptcpcomm_obj->rx_buf);
    
    pthread_join(ptcpcomm_obj->tid2, 0);
    
    /* 会话资源释放 */
    // ----------------------------------------
    // [说明]此处释放的会话资源不包括会话本身。
    // ----------------------------------------
TCPCOMM_EXIT_1:
    free(prx_cache);
    
TCPCOMM_EXIT_0:
    close(ptcpcomm_obj->comm_sock);
    ptcpcomm_obj->state = TCPCOMM_STAT_EXIT;
    OUTPUT("[TCPCOMM-INFO][ID:%d]TCP-Comm[%s:%d] closed.\n", ptcpcomm_obj->session_id, ptcpcomm_obj->remote_ip_address, ptcpcomm_obj->remote_ip_port);
    
    pthread_exit(NULL);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：tcp_comm_create
//|  功能描述：初始化TCP通信对象结构，为其创建TCP通信处理线程
//|  参数说明：用于输出套接字对象结构的指针和通信套接字描述符
//|  返回值说明：成功返回TCPCOMM_SUCCESS，pptcpcomm_obj指针输出指向对象结构体的空间；
//|              失败返回错误码（负值）
//|  备注：若创建套接字会话失败，不会修改原指针的值。
//|        只有成功创建了会话处理线程，才会为连接分配ID，因为ID是设计给后续会话轮询使用的。
//+------------------------------------------------------------------------------------------+
int tcp_comm_create(ST_TCP_COMM **pptcpcomm_obj, int comm_sock)
{
    int ret = 0;
    struct sockaddr_in local_ip_addr;
    struct sockaddr_in remote_ip_addr;
    socklen_t addr_len = (socklen_t)sizeof(struct sockaddr_in);
    
    /* 参数检查 */
    if(comm_sock < 0)
    {
        OUTPUT("[TCPCOMM-ERROR]The socket descriptor[%d] must be a positive integer!\n", comm_sock);
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 对象结构指针状态检查 */
    if(pptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Level 2 pointer can't be NULL!\n");
        close(comm_sock);
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    if(*pptcpcomm_obj != NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Level 1 pointer must be NULL!\n");
        close(comm_sock);
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    
    /* 申请套接字对象结构空间 */
    // ----------------------------------------
    // [说明]这里使用calloc保证了后续某些操作对
    //       结构体初始内存状态的要求。或也可以
    //       换用malloc + memset形式。
    // ----------------------------------------
    ST_TCP_COMM *ptmp = (ST_TCP_COMM *)calloc(1, sizeof(ST_TCP_COMM));
    if(ptmp == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Calloc[%d] ST_TCP_COMM structure failed!\n", sizeof(ST_TCP_COMM));
        return TCPCOMM_ERROR_MALLOC;
    }
    
    /* 初始化应用层缓冲区 */
    ret = ring_buffer_create(&ptmp->tx_buf, TX_BUF_SIZE);
    if(ret != RINGBUF_SUCCESS)
    {
        OUTPUT("[TCPCOMM-ERROR]Create TX_BUF failed with %#x!\n", ret);
        free(ptmp);
        close(comm_sock);
        return TCPCOMM_ERROR_BUFFER;
    }
    ret = ring_buffer_create(&ptmp->rx_buf, RX_BUF_SIZE);
    if(ret != RINGBUF_SUCCESS)
    {
        OUTPUT("[TCPCOMM-ERROR]Create RX_BUF failed with %#x!\n", ret);
        ring_buffer_destroy(&ptmp->tx_buf);
        free(ptmp);
        close(comm_sock);
        return TCPCOMM_ERROR_BUFFER;
    }
    
    /* 禁用发送缓冲区 */
    //int tmp = 0;
    //setsockopt(comm_sock, SOL_SOCKET, SO_SNDBUF, (char *)&tmp, sizeof(tmp));
    
    /* 设置当前套接字为非阻塞套接字 */
    //fcntl(comm_sock, F_SETFL, fcntl(comm_sock, F_GETFL, 0) | O_NONBLOCK);
    
    /* 更新套接字对象结构 */
    ptmp->comm_sock = comm_sock;
    
    getsockname(ptmp->comm_sock, (struct sockaddr *)&local_ip_addr,  &addr_len);
    getpeername(ptmp->comm_sock, (struct sockaddr *)&remote_ip_addr, &addr_len);
    inet_ntop(AF_INET, &local_ip_addr.sin_addr,  ptmp->local_ip_address,  sizeof(ptmp->local_ip_address));
    inet_ntop(AF_INET, &remote_ip_addr.sin_addr, ptmp->remote_ip_address, sizeof(ptmp->remote_ip_address));
    ptmp->local_ip_port  = ntohs(local_ip_addr.sin_port);
    ptmp->remote_ip_port = ntohs(remote_ip_addr.sin_port);
    
    /* 创建处理线程 */
    ptmp->flag_run = true;
    if(pthread_create(&ptmp->tid, NULL, tcp_communicate_process, (void *)ptmp) != 0)
    {
        OUTPUT("[TCPCOMM-ERROR]Create TCP communicate thread failed!\n");
        free(ptmp);
        close(comm_sock);
        return TCPCOMM_ERROR_SOCKET;
    }
    
    /* 更新套接字对象结构 */
    ptmp->state      = TCPCOMM_STAT_OK;
    ptmp->exit_state = TCPCOMM_SUCCESS;
    ptmp->session_id = session_base_id++;
    
    ptmp->stop              = stop;
    ptmp->send_data         = send_data;
    ptmp->get_data_len      = get_data_len;
    ptmp->get_data_point    = get_data_point;
    ptmp->update_data_point = update_data_point;
    ptmp->wait_for_data     = wait_for_data;
    ptmp->clear_tx_buf      = clear_tx_buf;
    ptmp->clear_rx_buf      = clear_rx_buf;
    
    *pptcpcomm_obj = ptmp;
    
    OUTPUT("[TCPCOMM-INFO][ID:%d]TCP-Comm[%s:%d] startup.\n", ptmp->session_id, ptmp->remote_ip_address, ptmp->remote_ip_port);
    
    return TCPCOMM_SUCCESS;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：tcp_comm_destroy
//|  功能描述：去初始化TCP通信对象结构，关闭其通信处理线程
//|  参数说明：指向套接字对象结构指针的指针
//|  返回值说明：成功返回TCPCOMM_SUCCESS，失败返回错误码（负值）
//|  备注：若销毁套接字会话成功，原指针的值会被置为NULL。
//+------------------------------------------------------------------------------------------+
int tcp_comm_destroy(ST_TCP_COMM **pptcpcomm_obj)
{
    /* 对象结构指针状态检查 */
    if(pptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Level 2 pointer can't be NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    if(*pptcpcomm_obj == NULL)
    {
        OUTPUT("[TCPCOMM-ERROR]Level 1 pointer can't be NULL!\n");
        return TCPCOMM_ERROR_ILLEGAL_PARAM;
    }
    if((*pptcpcomm_obj)->state != TCPCOMM_STAT_OK && (*pptcpcomm_obj)->state != TCPCOMM_STAT_EXIT)
    {
        OUTPUT("[TCPCOMM-ERROR][ID:%d]The current state[%d] does not support this operation!\n", (*pptcpcomm_obj)->session_id, (*pptcpcomm_obj)->state);
        return TCPCOMM_ERROR_UNEXPECT_STATE;
    }
    
    /* 关闭通信线程 */
    while((*pptcpcomm_obj)->state != TCPCOMM_STAT_EXIT)
    {
        /* 调用会话关闭方法 */
        (*pptcpcomm_obj)->stop(*pptcpcomm_obj);
        
        /* 等待通信（子）线程退出 */
        usleep(500000);
    }
    
    /* 释放套接字对象结构所占堆空间 */
    ring_buffer_destroy(&((*pptcpcomm_obj)->tx_buf));
    ring_buffer_destroy(&((*pptcpcomm_obj)->rx_buf));
    free(*pptcpcomm_obj);
    *pptcpcomm_obj = NULL;
    
    return TCPCOMM_SUCCESS;
}
